<template>
    <el-popover
        ref="listPopver"
        class="sc-f-c sc-pt-4 sc-pb-4"
        popper-class="sc-popper ScAtTextArea_popper"
        placement="top"
        trigger="manual"
        v-model="isOpenPopperVm"
        :visible-arrow="false"
    >
        <!-- 弹出popover-编辑Tag  -->

        <!-- <ul
            ref="memberList"
            class="atList sc-popper_list"
            :style="{ width: width + 'px' }"
            @mousedown.stop
            @click.stop
        >
            <li
                v-for="(atItem, index) in searchAtList"
                :key="atItem[atValue]"
                :class="{ active: index === selectedItemIndex }"
                :label="atItem[atLabel]"
                :value="atItem[atValue]"
                @click="handleChooseAtMember(atItem)"
            >
                <el-row type="flex" align="center">
                    {{ atItem[atLabel] }}
                    <span class="sc-text-info3 sc-pl-8 sc-pr-8">|</span>
                    <span class="sc-text-info3">{{ atItem[atValue] }}</span>
                </el-row>
            </li>
        </ul> -->
        <ul
            ref="memberList"
            class="atList sc-popper_list"
            :style="{ width: width + 'px' }"
            @mousedown.stop
            @click.stop
        >
             <sc-recycle-scroller
                class="scroller"
                style="max-height:220px;"
                :items="searchAtList"
                :item-size="30"
                :key-field="atValue"
                v-slot="{ item,index }"
            >
                 <li
                    :class="{ active: index === selectedItemIndex }"
                    :label="item[atLabel]"
                    :value="item[atValue]"
                    @click="handleChooseAtMember(item)"
                >
                    <el-row type="flex" align="center">
                        {{ item[atLabel] }}
                        <span class="sc-text-info3 sc-pl-8 sc-pr-8">|</span>
                        <span class="sc-text-info3">{{ item[atValue] }}</span>
                    </el-row>
                </li>
            </sc-recycle-scroller>
        </ul>

        <ScDivTextArea
            class="divTextArea sc-f-1"
            :class="textClass"
            ref="textarea"
            slot="reference"
            v-model="viewModel"
            v-bind="$attrs"
            @input="handleInput"
            @click.native="handleClickAreaText"
            @blur="hanldeBlur"
            @keydown.native.up="handleKeyDownUp"
            @keydown.native.down="handleKeyDownDown"
            @keydown.native.left="handleKeyDownLeft"
            @keydown.native.right="handleKeyDownRight"
            @keydown.native.enter="handleKeyDownEnter"
            @keydown.native.shift.50="handleKeyDownAddAtMember"
        ></ScDivTextArea>
    </el-popover>
</template>

<script>
    import { htmlDecodeByRegExp } from "@/utils/html";
    const RegAtMemberHtmlG = /<span[^>]+data-id=\"([^\"]+)\"[^>]+>[^<]+<[/]span>/g;
    const RegAtMemberHtml = /<span[^>]+data-id=\"([^\"]+)\"[^>]+>[^<]+<[/]span>/;

    /**
     * 1、点击input框，搜索结果，如果没有。关闭搜索结果
     */
    export default {
        name: "ScAtTextArea",
        inheritAttrs: false,
        components: {
        },
        props: {
            textClass: {
                type: String
            },
            value: {
                type: String,
                require: true
            },
            atList: {
                type: Array,
                require: true
            },
            atLabel: {
                type: String,
                require: true
            },
            atValue: {
                type: String,
                require: true
            }
        },
        data() {
            return {
                /** 打开界面的光标 */
                navigateRange: null,
                /** 解析输入文本 */
                isOpenPopper: false,
                width: 600,
                //搜索
                searchText: "",
                selectedItemIndex: -1,
            };
        },
        computed: {
            viewModel: {
                get() {
                    return this.value;
                },
                set(newValue) {
                    this.$emit("input", newValue);
                }
            },
            isOpenPopperVm: {
                get() {
                    return (
                        this.isOpenPopper &&
                        this.searchAtList &&
                        this.searchAtList.length > 0
                    );
                },
                set(newValue) {
                    this.isOpenPopper = newValue;
                }
            },
            /** 将viewModel分割 */
            viewModelList() {
                console.log(this.viewModelStr);
                const result = this.viewModelStr.match(RegAtMemberHtmlG);
                return result
                    .map(str => {
                        const memberId = str.match(RegAtMemberHtml)[1];
                        console.log("memberId", memberId);
                        if (memberId) {
                            var atMember = this.atList.find(
                                member => member[this.atValue] == memberId
                            );
                            if (atMember) {
                                return {
                                    type: "member",
                                    userId: atMember[this.atValue]
                                };
                            }
                        }
                    })
                    .filter(Boolean);
            },
            /** 界面显示的字符串。匹配 viewModel中的 @成员,添加html */
            viewModelStr() {
                return htmlDecodeByRegExp(this.viewModel);
            },
            searchAtList() {
                if (!this.searchText) {
                    return this.atList;
                }
                return this.atList.filter(
                    member => member[this.atLabel].indexOf(this.searchText) !== -1
                    || member[this.atValue].indexOf(this.searchText)!== -1 
                );
            },
            /** @ 过的用户 */
            atMapList() {
                return this.viewModelList
                    .filter(model => model.type === "member")
                    .map(member => member.userId);
            },
            $textArea() {
                return this.$refs.textarea.$el;
            }
        },
        methods: {
            /** 切换显示隐藏 */
            setPopperVisible(value) {
                this.selectedItemIndex = -1;
                this.isOpenPopper = value;
                if (value) {
                    let ctrl = this.$textArea;
                    this.width = Math.max(300, ctrl.clientWidth + 18);
                    ctrl.focus();
                }
            },
            /* 1、插入用户信息 */
            async handleChooseAtMember(atMember) {
                this.focus();
                this.insertAtMember(atMember, this.navigateRange);
            },
            async insertAtMember(atMember, atRange) {
                this.setPopperVisible(false);
                // 监听返回的@人物 添加@ 数据
                // 先丢到val 里面去 利用set去重
                console.log("监听到@回来了", atMember);
                this.insertAtMemberAtPosition(atMember, atRange);
                await this.$nextTick();
                this.resetCursor();
                this.searchText = "";
                this.navigateRange = null;
                this.$emit("changeAt", this.atMapList);
            },
            /** 2、外部手动调用弹框选择 */
            startChoose() {
                let ctrl = this.$textArea;
                ctrl.focus();
                this.inputType = "add";
                console.log("this.cursorPostion",this.cursorPostion);
                const position  = this.cursorPostion || this.getCursorPosition();
                this.cursorPostion = null;
                this.insertTextAtPosition("@", position);
            },
            /** 3、点击用户移动光标 */
            handleClickAreaText() {
                this.resetCursor();
            },
            /** 5、输入字符时，判断是否为可编辑区域，移动到最近的可编辑区域 */
            async handleKeyDown(e) {
                if (e.key === "Process") {
                    return;
                }
                this.resetCursor();
            },
            /** 4、 */
            async handleKeyDownUp(e) {
                this.inputType = "up";
                if (!this.isOpenPopperVm) {
                    return;
                }
                this.selectedItemIndex--;
                if (this.selectedItemIndex < 0) {
                    this.selectedItemIndex = this.searchAtList.length - 1;
                }
                e.preventDefault();
                await this.$nextTick();
                this.$refs.memberList
                    .querySelector(".active")
                    .scrollIntoView({ block: "nearest" });
            },
            /** 5、 */
            async handleKeyDownDown(e) {
                this.inputType = "down";
                if (!this.isOpenPopperVm) {
                    return;
                }
                this.selectedItemIndex++;
                if (this.selectedItemIndex > this.searchAtList.length - 1) {
                    this.selectedItemIndex = 0;
                }
                e.preventDefault();
                await this.$nextTick();
                this.$refs.memberList
                    .querySelector(".active")
                    .scrollIntoView({ block: "nearest" });
            },
            /** 5、 */
            async handleKeyDownLeft(e) {
                this.inputType = "left";
                //获取光标后的位置
                setTimeout(() => this.resetAtSearchList(), 0);
            },
            /** 5、 */
            async handleKeyDownRight(e) {
                this.inputType = "right";
                console.group("handleKeyDownRight");
                //获取光标后的位置
                setTimeout(() => {
                    this.resetCursor();
                    this.resetAtSearchList();
                }, 0);
                console.groupEnd();
            },
            /** 5、 */
            async handleKeyDownEnter(e) {
                this.inputType = "enter";
                if (!this.isOpenPopperVm) {
                    return;
                }
                if (this.selectedItemIndex < 0) {
                    e.preventDefault();
                    return;
                }
                const atMember = this.searchAtList[this.selectedItemIndex];
                if (!atMember) {
                    return;
                }
                this.insertAtMember(atMember, this.navigateRange);
                e.preventDefault();
                this.closePopper();
            },
            async handleKeyDownAddAtMember() {
                console.log("handleKeyDownAddAtMember");
                this.inputType = "add";
            },
            /** 输入框改变时，根据按键执行操作 */
            handleInput() {
                switch (this.inputType) {
                    case "add":
                        // 增加 这里不在 @input 里面判断了
                        let range = this.getCursorPosition();

                        //输入@后重建索引
                        this.navigateRange = range;
                        // 跳转选择用户的页面
                        this.searchText = "";
                        this.setPopperVisible(true);
                        break;
                    case "up":
                    case "down":
                    case "left":
                    case "right":
                    case "enter":
                        break;
                    default:
                        this.resetAtSearchList();
                        break;
                }
                this.inputType = "";
            },
            /** 当光标和输入内容移动时，重置搜索的@用户 */
            async resetAtSearchList() {
                const range = this.getCursorPosition();
                console.log("this.navigateRange", this.navigateRange);
                if (!this.navigateRange) {
                    this.closePopper();
                    return;
                }
                console.log(range.endContainer, this.navigateRange.endContainer);
                if (range.endContainer !== this.navigateRange.endContainer) {
                    this.closePopper();
                    return;
                }
                console.log(
                    "this.navigateRange.endOffset",
                    this.navigateRange.endOffset
                );
                console.log("range.endOffset", range.endOffset);
                if (range.endOffset < this.navigateRange.endOffset) {
                    this.closePopper();
                    return;
                }
                if (!range.endContainer.nodeValue) {
                    this.closePopper();
                    return;
                }
                this.setPopperVisible(true);
                this.searchText = range.endContainer.nodeValue.substring(
                    this.navigateRange.endOffset,
                    range.endOffset
                );
            },
            /** 当选中特殊的节点时，重置光标到后面的节点 */
            resetCursor() {
                const range = this.getCursorPosition();
                console.log("resetCursor 旧 range", range);
                const textNode = range.endContainer;
                if (textNode.className === "atMember") {
                    //atMember>text 不能编辑，需要跳过
                    if (range.endOffset > 1) {
                        //文本节点，重置到最近的父亲
                        this.setCursorAfterNodeByParentNode(textNode);
                    } else {
                        this.setCursorBeforeNodeByParentNode(textNode);
                    }
                    return true;
                }
                if (textNode.parentElement.className === "atMember") {
                    //atMember>text 不能编辑，需要跳过
                    if (range.endOffset > 1) {
                        //文本节点，重置到最近的父亲
                        this.setCursorAfterNodeByParentNode(textNode.parentElement);
                    } else {
                        this.setCursorBeforeNodeByParentNode(
                            textNode.parentElement
                        );
                    }
                    return true;
                }
                return false;
            },
            /* 获取光标位置 */
            getCursorPosition() {
                var selection = getSelection();
                var range = selection.getRangeAt(0);
                return range;
            },
            hanldeBlur(){
                this.cursorPostion = this.getCursorPosition();
            },
            closePopper() {
                this.setPopperVisible(false);
            },
            //在指定位置插入一个文字
            async insertTextAtPosition(addStr, atRange) {
                const selection = getSelection();
                // 清除选定对象的所有光标对象
                selection.removeAllRanges();
                // 获取光标对象的范围界定对象，一般就是textNode对象
                var textNode = atRange.startContainer;
                // 判断选定对象范围是编辑框还是文本节点
                if (textNode.nodeName === "#text") {
                    // 获取光标位置
                    var rangeStartOffset = atRange.startOffset;
                    // 文本节点在光标位置处插入新的表情内容
                    textNode.insertData(rangeStartOffset, addStr);
                    // 光标移动到到原来的位置加上新内容的长度
                    atRange.setStart(textNode, rangeStartOffset + addStr.length);
                    // 光标开始和光标结束重叠
                    atRange.collapse(true);
                    // 插入新的光标对象
                    selection.addRange(atRange);
                } else {
                    console.log("selection", selection);
                    var newStrNode = document.createTextNode(addStr);
                    if (textNode.childNodes.length > 0) {
                        //如果光标应该放在@member后面，这直接在当前光标位置后面加一个元素
                        console.log(
                            "在后面添加节点",
                            textNode.childNodes[atRange.endOffset]
                        );
                        textNode.insertBefore(
                            newStrNode,
                            textNode.childNodes[atRange.endOffset]
                        );
                    } else {
                        // 否则直接插入一个元素
                        textNode.appendChild(newStrNode);
                    }
                    // 创建新的光标对象
                    var range = document.createRange();
                    // 光标对象的范围界定为新建的表情节点
                    range.selectNodeContents(newStrNode);
                    // 光标位置定位在表情节点的最大长度
                    range.setStart(newStrNode, addStr.length);
                    // 使光标开始和光标结束重叠
                    range.collapse(true);
                    // 插入新的光标对象
                    selection.addRange(range);
                }
                //从文本框同步ViewModel
                this.$refs.textarea.inputText();
            },
            //在指定位置插入一个@成员
            async insertAtMemberAtPosition(addMember, atRange) {
                let addStr = addMember[this.atLabel];
                // 获取光标对象的范围界定对象，一般就是textNode对象
                var textNode = atRange.startContainer;
                //开始位置
                const atPosition = atRange.startOffset - 1; //需要从 @ 开始
                if (atPosition > 0) {
                    const beforeStr = textNode.nodeValue.substring(0, atPosition);
                    const newBeforeTextNode = document.createTextNode(beforeStr);
                    textNode.parentElement.insertBefore(
                        newBeforeTextNode,
                        textNode
                    );
                }
                const span = document.createElement("span");
                span.innerText = "@" + addStr;
                span.setAttribute("data-id", addMember[this.atValue]);
                span.setAttribute("class", "atMember");
                span.setAttribute("contenteditable", "false");
                textNode.parentElement.insertBefore(span, textNode);

                // 结束位置
                const afterStr = textNode.nodeValue.substring(
                    atRange.endOffset + this.searchText.length
                ); //插入光标位置
                if (afterStr) {
                    const newAfterTextNode = document.createTextNode(afterStr);
                    textNode.parentElement.insertBefore(newAfterTextNode, textNode);
                }
                textNode.remove();

                this.setCursorAfterNodeByParentNode(span);
                //从文本框同步ViewModel
                this.$refs.textarea.inputText();
            },
            setCursorBeforeNodeByParentNode(span) {
                console.log("将光标放在该标签前面：", span);
                let i = 0; //当前光标在父元素中的位置
                // 如果文本框的子元素大于0，则表示有其他元素，则按照位置插入表情节点
                for (; i < span.parentElement.childNodes.length; i++) {
                    if (span.parentElement.childNodes[i] == span) {
                        break;
                    }
                }
                // 创建新的光标对象
                var range = document.createRange();
                // 光标对象的范围界定为新建的表情节点
                range.selectNodeContents(span.parentElement);
                console.log(span.parentElement.childNodes);
                // 光标位置定位在表情节点的最大长度
                range.setStart(span.parentElement, i);
                range.collapse(true);
                console.log("新的位置", range);
                const selection = getSelection();
                // 清除选定对象的所有光标对象
                selection.removeAllRanges();
                // 插入新的光标对象
                selection.addRange(range);
            },
            setCursorAfterNodeByParentNode(span) {
                console.log("将光标放在该标签后面：", span);
                let i = 0; //当前光标在父元素中的位置
                // 如果文本框的子元素大于0，则表示有其他元素，则按照位置插入表情节点
                for (; i < span.parentElement.childNodes.length; i++) {
                    if (span.parentElement.childNodes[i] == span) {
                        break;
                    }
                }
                // 创建新的光标对象
                var range = document.createRange();
                // 光标对象的范围界定为新建的表情节点
                range.selectNodeContents(span.parentElement);
                console.log(span.parentElement.childNodes);
                // 光标位置定位在表情节点的最大长度
                range.setStart(span.parentElement, i + 1);
                range.collapse(true);
                console.log("新的位置", range);
                const selection = getSelection();
                // 清除选定对象的所有光标对象
                selection.removeAllRanges();
                // 插入新的光标对象
                selection.addRange(range);
            },
            /** 2、外部手动调用弹框选择 */
            focus() {
                let ctrl = this.$textArea;
                ctrl.focus();
            }
        },
        watch: {
            async searchAtList() {
                if (!this.isOpenPopper) {
                    return;
                }
                await this.$nextTick();
                //因为数据改变会导致高度改变
                this.$refs.listPopver.updatePopper();
            }
        },
        mounted() {
            window.addEventListener("click", this.closePopper);
        },
        destroyed() {
            window.removeEventListener("click", this.closePopper);
        }
    };
</script>
<style lang="scss">
    .ScAtTextArea_popper {
        transform: translateY(-8px);
    }
</style>
<style scoped lang="scss">
    @import "@/styles/variables.scss";
    ul.atList {
        list-style: none;
        max-height: 220px;
        overflow: auto;
        padding: 0;
        margin: 0;
        li {
            padding-left: 8px;
            border-radius: 4px;
            cursor: pointer;
            &:hover,
            &.active {
                color: $green;
                background-color: $bgColorGray;
            }
        }
    }
    .divTextArea {
        color: $text;
        font-size: 14px;
        line-height: 20px;
        overflow: auto;
        word-break: break-all;
        ::v-deep .atMember {
            color: $green;
            caret-color: $text;
            padding-left: 2px;
            padding-right: 2px;
            font-size: 14px;
        }
    }
</style>
